﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

namespace Neto;

/// <summary>
///     提供了基于表达式树的静态转换功能，允许在运行时动态构建从一种类型到另一种类型的转换函数。
/// </summary>
/// <typeparam name="TIn">输入类型的泛型参数。</typeparam>
/// <typeparam name="TOut">输出类型的泛型参数。</typeparam>
public static class DeepCloneExtension<TIn, TOut>
{
    /// <summary>
    ///     缓存的转换函数，用于避免重复构建相同的转换逻辑。
    /// </summary>
    private static readonly Func<TIn, TOut> cache = GetFunc();

    /// <summary>
    ///     动态构建并返回一个从TIn到TOut的转换函数。
    /// </summary>
    /// <returns>从TIn到TOut的转换函数。</returns>
    private static Func<TIn, TOut> GetFunc()
    {
        // 创建输入参数的表达式
        var parameterExpression = Expression.Parameter(typeof(TIn), "p");

        // 构建一个新实例的表达式
        var newExpression = Expression.New(typeof(TOut));

        // 遍历输出类型的所有可写属性
        var memberInitExpression = Expression.MemberInit(newExpression, (from item in typeof(TOut).GetProperties()
            where item.CanWrite
            let property = Expression.Property(parameterExpression, typeof(TIn).GetProperty(item.Name))
            select Expression.Bind(item, property)).Cast<MemberBinding>().ToArray());

        // 创建一个lambda表达式，用于从TIn转换到TOut
        var lambda =
            Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, parameterExpression);

        // 编译lambda表达式为可执行的函数
        return lambda.Compile();
    }

    /// <summary>
    ///     应用缓存的转换函数将输入对象转换为输出对象。
    /// </summary>
    /// <param name="tIn">待转换的输入对象。</param>
    /// <returns>转换后的输出对象。</returns>
    public static TOut Trans(TIn tIn)
    {
        // 使用缓存的转换函数进行转换
        return cache(tIn);
    }
}